`include "define.svh"

module mycpu(
    input wire                              clk,
    input wire                              resetn,
    input wire [5 : 0]                      ext_int,
    
    output reg                              inst_req,
    output reg                              inst_wr,
    output reg [1 : 0]                      inst_size,
    output reg [`PC_WIDTH - 1 : 0]          inst_addr,
    output reg [3 : 0]                      inst_wstrb,
    output reg [`REG_WIDTH - 1 : 0]         inst_wdata,
    input wire                              inst_addr_ok,
    input wire                              inst_data_ok,
    input wire [`REG_WIDTH - 1 : 0]         inst_rdata,
    
    output reg                              data_req,
    output reg                              data_wr,
    output reg [1 : 0]                      data_size,
    output reg [`PC_WIDTH - 1 : 0]          data_addr,
    output reg [3 : 0]                      data_wstrb,
    output reg [`REG_WIDTH - 1 : 0]         data_wdata,
    input wire                              data_addr_ok,
    input wire                              data_data_ok,
    input wire [`REG_WIDTH - 1 : 0]         data_rdata,
    
    output reg [`PC_WIDTH - 1 : 0]          debug_wb_pc,
    output reg [3 : 0]                      debug_wb_rf_wen,
    output reg [`REG_ADDR_WIDTH - 1 : 0]    debug_wb_rf_wnum,
    output reg [`REG_WIDTH - 1 : 0]         debug_wb_rf_wdata
    );
    
    // stall output to stages
    wire                                    ctrl_if_stall;
    wire                                    ctrl_id_stall;
    wire                                    ctrl_ex_stall;
    wire                                    ctrl_mem_stall;
    wire                                    ctrl_wb_stall;
    // stall request from stages
    wire                                    div_stall_request;
    wire                                    load_use_stall_request;
    wire                                    inst_stall_request;
    wire                                    data_stall_request;
    // flush signal to ID and EX stages
    wire                                    pipeline_flush;
    // Exception signal to stages
    wire                                    is_exc;
    wire                                    is_eret;
    wire                                    is_Adexc;
    wire [`PC_WIDTH - 1 : 0]                ctrl2pc_epc;
    wire                                    branch_flag;
    wire [`PC_WIDTH - 1 : 0]                branch_addr;
    wire [`PC_WIDTH - 1 : 0]                pc_pcreg;
    wire [`REG_WIDTH - 1 : 0]               inst_pcreg;
    
    // SRAM-like bus
    wire                                    inst_sram_en;
    wire [`REG_WIDTH - 1 : 0]               inst_sram_addr;
    wire                                    inst_addr_ok_inn;
    wire                                    inst_data_ok_inn;
    wire                                    data_sram_en;
    wire [3 : 0]                            data_sram_wen;
    wire [`REG_WIDTH - 1 : 0]               data_sram_addr;
    wire [`REG_WIDTH - 1 : 0]               data_sram_wdata;
    wire                                    data_addr_ok_inn;
    wire                                    data_data_ok_inn;
    
    pc_reg pc_reg0(
        .rst                (resetn             ),
        .clk                (clk                ),
        .ctrl_stall_i       (ctrl_if_stall      ),
        .branch_flag_i      (branch_flag        ),
        .branch_addr_i      (branch_addr        ),
        .data_ok            (inst_data_ok_inn   ),
        .addr_ok            (inst_addr_ok_inn   ),
        .is_exc_i           (is_exc             ),
        .is_eret_i          (is_eret            ),
        .epc_i              (ctrl2pc_epc        ),
        
        .inst_sram_en       (inst_sram_en       ),
        .inst_sram_addr     (inst_sram_addr     ),
        .inst_sram_rdata    (inst_rdata         ),
        
        .inst_o             (inst_pcreg         ),
        .pc_o               (pc_pcreg           )
    );
    
    
    wire [`REG_WIDTH - 1 : 0]       reg_rdata1;         // register read #1 from register to id-stage
    wire [`REG_WIDTH - 1 : 0]       reg_rdata2;         // register read #2 from register to id-stage
    
    wire                            ex_wenable;     // ex-stage signal pass to mem-stage & forwarding
    wire [`REG_ADDR_WIDTH - 1 : 0]  ex_waddr;       // ex-stage signal pass to mem-stage & forwarding
    wire [`REG_WIDTH - 1 : 0]       ex_wdata;       // ex-stage signal pass to mem-stage & forwarding
    
    wire                            mem_wenable;    // mem-stage signal pass to wb-stage & forwarding
    wire [`REG_ADDR_WIDTH - 1 : 0]  mem_waddr;      // mem-stage signal pass to wb-stage & forwarding
    wire [`REG_WIDTH - 1 : 0]       mem_wdata;      // mem-stage signal pass to wb-stage & forwarding
    
    wire [`REG_ADDR_WIDTH - 1 : 0]  reg_raddr1;     // register read address #1 from id-stage to register
    wire [`REG_ADDR_WIDTH - 1 : 0]  reg_raddr2;     // register read address #2 from id-stage to register
    
    wire [`REG_WIDTH - 1 : 0]       opprand1;       // opprand #1 from id-stage to ex-stage
    wire [`REG_WIDTH - 1 : 0]       opprand2;       // opprand #2 from id-stage to ex-stage
    wire [`ALUOP_WIDTH - 1 : 0]     aluop;          // alu opcode from id-stage to ex-stage
    wire [`REG_ADDR_WIDTH - 1 : 0]  id_wregaddr;    // write reg address from id-stage to ex-stage
    wire                            id_wregenable;  // write reg enable from id-stage to ex-stage   
    
    wire [`REG_WIDTH - 1 : 0]       rdata_hi;   // HI-data read from HILO-reg to id-stage
    wire [`REG_WIDTH - 1 : 0]       rdata_lo;   // LO-data read from HILO-reg to id-stage
    
    
    wire                            whien;          // HI reg write enable from ex-stage to HILO register
    wire                            wloen;          // LO reg write enable from ex-stage to HILO register
    wire [`REG_WIDTH - 1 : 0]       whidata;        // HI reg write data from ex-stage to HILO register
    wire [`REG_WIDTH - 1 : 0]       wlodata;        // LO reg write data from ex-stage to HILO register
    
    wire                            id2ex_whien;        // whien from id-stage to ex-stage
    wire                            id2ex_wloen;        // wloen from id-stage to ex-stage
    wire [`REG_WIDTH - 1 : 0]       id2ex_whilodata;    // whilodata from id-stage to ex-stage
    
    wire [3 : 0]                    id2ex_rdataen;      // id-stage to ex-stage: DataSRAM write to reg
    wire                            id2ex_rdatasigned;  // id-stage to ex-stage: data extension sel
    wire [3 : 0]                    id2ex_wdataen;      // id-stage to ex-stage: write to DataSRAM enable
    wire [`REG_WIDTH - 1 : 0]       id2ex_wdata;        // id-stage to ex-stage: write to DataSRAM data
    
    wire [3 : 0]                    data2reg_en;  // DataSRAM write to reg enable from ex-stage to mem-stage
    
    wire [`PC_WIDTH - 1 : 0]        pc_id2ex;
    
    wire [`REG_ADDR_WIDTH - 1 : 0]  cp0_raddr;          // CP0 reg read addr from id to cp0
    wire [`REG_WIDTH - 1 : 0]       cp0_rdata;          // CP0 reg read data from cp0 to id
    
    wire id2ex_pc_in_delay_slot;        // If pc in in delay slot from id-stage to ex-stage
    wire [`EXCCODE_WIDTH - 1 : 0]   id2ex_exc_code;
    
    wire                            id2ex_cp0_wen;
    wire [`REG_ADDR_WIDTH - 1 : 0]  id2ex_cp0_waddr;
    wire [`REG_WIDTH - 1 : 0]       id2ex_cp0_wdata;
    
    wire                            mtc_wen;            // MTC0 instr
    wire [`REG_ADDR_WIDTH - 1 : 0]  mtc_waddr;          // MTC0 instr
    wire [`REG_WIDTH - 1 : 0]       mtc_wdata;          // MTC0 instr
    
    wire [`REG_WIDTH - 1 : 0]       id2ex_error_pc;
    
    wire                            cp0_status_exl;    // also send to ID
    wire                            cp0_status_ie;
    wire [7 : 0]                    cp0_status_im;
    wire [5 : 0]                    cp0_cause_ip_7_2;
    wire [1 : 0]                    cp0_cause_ip_1_0;
    wire [31 : 0]                   cp0_epc;
    
    id_wrap id_stage(
        .clk                    (clk                    ),
        .rstn                   (resetn                 ),
        .hardware_int           (ext_int                ),
        .software_int           (cp0_cause_ip_1_0       ),
        .cp0_status_im          (cp0_status_im          ),
        .cp0_status_ie          (cp0_status_ie          ),
        .cp0_status_exl         (cp0_status_exl         ),
        .ctrl_stall_i           (ctrl_id_stall          ),
        .pipeline_flush_i       (pipeline_flush         ),
        .addr_ok                (inst_addr_ok_inn       ),
        .data_ok                (inst_data_ok_inn       ),
        .inst_i                 (inst_pcreg             ),
        .pc_i                   (pc_pcreg               ),
        .reg_rdata1_i           (reg_rdata1             ),
        .reg_rdata2_i           (reg_rdata2             ),
        .rhidata_i              (rdata_hi               ),
        .rlodata_i              (rdata_lo               ),
        .ex_whien_i             (whien                  ),
        .ex_whidata_i           (whidata                ),
        .ex_wloen_i             (wloen                  ),
        .ex_wlodata_i           (wlodata                ),
        .ex_wenable_i           (ex_wenable             ),
        .ex_waddr_i             (ex_waddr               ),
        .ex_wdata_i             (ex_wdata               ),
        .ex_data2reg_en_i       (data2reg_en            ),
        .ex_cp0_wen_i           (mtc_wen                ),
        .ex_cp0_waddr_i         (mtc_waddr              ),
        .ex_cp0_wdata_i         (mtc_wdata              ),
        .mem_wenable_i          (mem_wenable            ),
        .mem_waddr_i            (mem_waddr              ),
        .mem_wdata_i            (mem_wdata              ),
        .raddr1_o               (reg_raddr1             ),
        .raddr2_o               (reg_raddr2             ),
        .opprand1_o             (opprand1               ),
        .opprand2_o             (opprand2               ),
        .aluop_o                (aluop                  ),
        .wregaddr_o             (id_wregaddr            ),
        .wregenable_o           (id_wregenable          ),
        .whien_o                (id2ex_whien            ),
        .wloen_o                (id2ex_wloen            ),
        .whilodata_o            (id2ex_whilodata        ),
        .branch_flag_o          (branch_flag            ),
        .branch_addr_o          (branch_addr            ),
        .rdataen_o              (id2ex_rdataen          ),
        .rdatasigned_o          (id2ex_rdatasigned      ),
        .wdataen_o              (id2ex_wdataen          ),
        .wdata_o                (id2ex_wdata            ),
        .debug_pc_o             (pc_id2ex               ),
        .pc_in_delay_slot       (id2ex_pc_in_delay_slot ),
        .exc_code               (id2ex_exc_code         ),
        .error_pc               (id2ex_error_pc         ),
        .load_use_stall_request (load_use_stall_request ),
        .inst_stall_request     (inst_stall_request     ),
        .cp0_raddr_o            (cp0_raddr              ),
        .cp0_rdata_i            (cp0_rdata              ),
        .mtc_wen                (id2ex_cp0_wen          ),
        .mtc_waddr              (id2ex_cp0_waddr        ),
        .mtc_wdata              (id2ex_cp0_wdata        )
    );
    
    wire                            wb_wregenable;      // register write enable from wb-stage to register
    wire [`REG_ADDR_WIDTH - 1 : 0]  wb_wregaddr;        // register write address from wb-stage to register
    wire [`REG_WIDTH - 1 : 0]       wb_wdata;           // register write data from wb-stage to register
    
    register register0(
        .clk        (clk            ),
        
        .wen_i      (wb_wregenable  ),
        .waddr_i    (wb_wregaddr    ),
        .wdata_i    (wb_wdata       ),
        
        .raddr1_i   (reg_raddr1     ),
        .rdata1_o   (reg_rdata1     ),
        
        .raddr2_i   (reg_raddr2     ),
        .rdata2_o   (reg_rdata2     )
    );
    
    wire                            data2reg_signed;    // DataSRAM write to reg extend sel from ex to mem
    wire [4 : 0]                    data2reg_shift;     // DataSRAM write to reg data shift offset from ex to mem
    
    wire [`PC_WIDTH - 1 : 0]        pc_ex2mem;
    wire [`REG_WIDTH - 1 : 0]       data_ex2mem;
    
    wire                            ex_pc_in_delay_slot; // If pc is in delay slot from ex-stage to mem-stage
    wire [`EXCCODE_WIDTH - 1 : 0]   ex_exc_code;
    wire [`PC_WIDTH - 1 : 0]        ex_badvaddr;
    
    ex_wrap ex_stage(
        .clk                (clk                    ),
        .rstn               (resetn                 ),
        .id_stalled         (ctrl_id_stall          ),
        .ctrl_stall         (ctrl_ex_stall          ),
        .pipeline_flush_i   (pipeline_flush         ),
        .addr_ok            (data_addr_ok_inn       ),
        .data_ok            (data_data_ok_inn       ),
        .debug_pc_i         (pc_id2ex               ),          
        .pc_in_delay_slot_i (id2ex_pc_in_delay_slot ),
        .exc_code_i         (id2ex_exc_code         ),
        .error_pc_i         (id2ex_error_pc         ),
        .opprand1_i         (opprand1               ),            
        .opprand2_i         (opprand2               ),            
        .aluop_i            (aluop                  ), 
        .wregaddr_i         (id_wregaddr            ),       
        .wregenable_i       (id_wregenable          ),  
        .id_whien_i         (id2ex_whien            ),        
        .id_wloen_i         (id2ex_wloen            ),        
        .id_whilodata_i     (id2ex_whilodata        ), 
        .debug_pc_o         (pc_ex2mem              ),
        .pc_in_delay_slot_o (ex_pc_in_delay_slot    ),
        .exc_code_o         (ex_exc_code            ),
        .badvaddr_o         (ex_badvaddr            ),
        .wregaddr_o         (ex_waddr               ),        
        .wregenable_o       (ex_wenable             ),        
        .wregdata_o         (ex_wdata               ),            
        .whireg_o           (whidata                ),               
        .wloreg_o           (wlodata                ),
        .data_o             (data_ex2mem            ),             
        .whien_o            (whien                  ),               
        .wloen_o            (wloen                  ),  
        .rdataen_i          (id2ex_rdataen          ),       
        .rdatasigned_i      (id2ex_rdatasigned      ),  
        .wdataen_i          (id2ex_wdataen          ),      
        .wdata_i            (id2ex_wdata            ), 
        .cp0_wen_i          (id2ex_cp0_wen          ),
        .cp0_waddr_i        (id2ex_cp0_waddr        ),
        .cp0_wdata_i        (id2ex_cp0_wdata        ),
        .cp0_wen_o          (mtc_wen                ),
        .cp0_waddr_o        (mtc_waddr              ),
        .cp0_wdata_o        (mtc_wdata              ),
        .data_sram_en       (data_sram_en           ),
        .data_sram_wen      (data_sram_wen          ),
        .data_sram_addr     (data_sram_addr         ),
        .data_sram_wdata    (data_sram_wdata        ),
        .data_sram_rdata    (data_rdata             ),
        .data2reg_en        (data2reg_en            ),
        .data2reg_signed    (data2reg_signed        ),
        .data2reg_shift     (data2reg_shift         ),
        .div_stall          (div_stall_request      ),
        .data_stall         (data_stall_request     )
    ); 
    
    hilo_reg hilo_register(
        .clk                (clk        ),
        .rst                (resetn     ),
        
        .whienable_i        (whien      ), 
        .wloenable_i        (wloen      ), 
        .whidata_i          (whidata    ), 
        .wlodata_i          (wlodata    ), 
        
        .rhidata_o          (rdata_hi   ), 
        .rlodata_o          (rdata_lo   )
    );
    
    wire [`PC_WIDTH - 1 : 0]    pc_mem2wb;
    
    mem_wrap mem_stage(
        .clk                    (clk                    ),
        .rstn                   (resetn                 ),
        .ex_stalled             (ctrl_ex_stall          ),
        .ctrl_stall             (ctrl_mem_stall         ),
        .debug_pc_i             (pc_ex2mem              ),
        .data_data_ok           (data_data_ok_inn       ),  
        .data2reg_en            (data2reg_en            ),     
        .data2reg_signed        (data2reg_signed        ),
        .data2reg_shift         (data2reg_shift         ), 
        .rdata_i                (data_ex2mem            ),
        .wregaddr_i             (ex_waddr               ), 
        .wregenable_i           (ex_wenable             ),    
        .wregdata_i             (ex_wdata               ),       
        .wregaddr_o             (mem_waddr              ),     
        .wregenable_o           (mem_wenable            ),  
        .wregdata_o             (mem_wdata              ), 
        .debug_pc_o             (pc_mem2wb              )
    );
    
    wb_wrap wb_stage(
        .clk                    (clk                    ),
        .rstn                   (resetn                 ),
        .ctrl_stall             (ctrl_wb_stall          ),
        .debug_pc_i             (pc_mem2wb              ),
        .wregaddr_i             (mem_waddr              ),     
        .wregenable_i           (mem_wenable            ),    
        .wregdata_i             (mem_wdata              ),        
        .wregaddr_o             (wb_wregaddr            ),     
        .wregenable_o           (wb_wregenable          ),  
        .wregdata_o             (wb_wdata               ),  
        .debug_wb_pc            (debug_wb_pc            ),
        .debug_wb_rf_wen        (debug_wb_rf_wen        ),
        .debug_wb_rf_wnum       (debug_wb_rf_wnum       ),
        .debug_wb_rf_wdata      (debug_wb_rf_wdata      )
    );
    
    
    ctrl ctrl0(
        .div_stall_request      (div_stall_request      ),       
        .load_use_stall_request (load_use_stall_request ),
        .inst_stall_request     (inst_stall_request     ),
        .data_stall_request     (data_stall_request     ),
        .if_stage_stall         (ctrl_if_stall          ),       
        .id_stage_stall         (ctrl_id_stall          ),        
        .ex_stage_stall         (ctrl_ex_stall          ),       
        .mem_stage_stall        (ctrl_mem_stall         ),       
        .wb_stage_stall         (ctrl_wb_stall          )        
    );
    
    
    wire [31 : 0]                     epc_wdata;
    wire                              cause_bd_wdata;
    wire                              status_exl_wdata;
    wire [31 : 0]                     badvaddr_wdata;
    wire [4 : 0]                      cause_excode_wdata;
    
    cp0 cp0_reg(
        .clk                    (clk                    ),
        .rstn                   (resetn                 ),
        .ext_int                (ext_int                ),
        .mfc_raddr              (cp0_raddr              ),
        .mfc_rdata              (cp0_rdata              ),
        .mtc_wen                (mtc_wen                ),
        .mtc_waddr              (mtc_waddr              ),
        .mtc_wdata              (mtc_wdata              ),
        .status_exl_o           (cp0_status_exl         ),
        .status_ie_o            (cp0_status_ie          ),
        .status_im_o            (cp0_status_im          ),
        .cause_ip_7_2_o         (cp0_cause_ip_7_2       ),
        .cause_ip_1_0_o         (cp0_cause_ip_1_0       ),
        .epc_o                  (cp0_epc                ),
        .is_exc                 (is_exc                 ),
        .is_eret                (is_eret                ),
        .is_Adexc               (is_Adexc               ),
        .epc_wdata              (epc_wdata              ),
        .cause_bd_wdata         (cause_bd_wdata         ),
        .status_exl_wdata       (status_exl_wdata       ),
        .badvaddr_wdata         (badvaddr_wdata         ),
        .cause_excode_wdata     (cause_excode_wdata     )
    );
    
    exc_ctrl exc_ctrl0(
        .exc_pc                 (pc_ex2mem              ),
        .exc_code               (ex_exc_code            ),
        .exc_pc_in_delay_slot   (ex_pc_in_delay_slot    ),
        .badvaddr               (ex_badvaddr            ),
        .status_exl_i           (cp0_status_exl         ),
        .epc_i                  (cp0_epc                ),
        .pipeline_flush         (pipeline_flush         ),
        .is_exc                 (is_exc                 ),
        .is_eret                (is_eret                ),
        .is_Adexc               (is_Adexc               ),
        .pc_in_epc              (ctrl2pc_epc            ),
        .epc_wdata              (epc_wdata              ),
        .cause_bd_wdata         (cause_bd_wdata         ),
        .status_exl_wdata       (status_exl_wdata       ),
        .badvaddr_wdata         (badvaddr_wdata         ),
        .cause_excode_wdata     (cause_excode_wdata     )
    );
    
    inst_sram_like_interface inst_sram_like(
        .inst_sram_en           (inst_sram_en           ),
        .inst_sram_addr         (inst_sram_addr         ),
        .inst_req               (inst_req               ),
        .inst_wr                (inst_wr                ),
        .inst_size              (inst_size              ),
        .inst_addr              (inst_addr              ),
        .inst_wstrb             (inst_wstrb             ),
        .inst_wdata             (inst_wdata             ),
        .inst_addr_ok_i         (inst_addr_ok           ),
        .inst_data_ok_i         (inst_data_ok           ),
        .inst_addr_ok_o         (inst_addr_ok_inn       ),
        .inst_data_ok_o         (inst_data_ok_inn       )
    );
    
    data_sram_like_interface data_sram_like(
        .data_sram_en           (data_sram_en           ),
        .data_sram_wen          (data_sram_wen          ),
        .data_sram_addr         (data_sram_addr         ),
        .data_sram_wdata        (data_sram_wdata        ),
        .data_req               (data_req               ),
        .data_wr                (data_wr                ),
        .data_size              (data_size              ),
        .data_addr              (data_addr              ),
        .data_wstrb             (data_wstrb             ),
        .data_wdata             (data_wdata             ),
        .data_addr_ok_i         (data_addr_ok           ),
        .data_data_ok_i         (data_data_ok           ),
        .data_addr_ok_o         (data_addr_ok_inn       ),
        .data_data_ok_o         (data_data_ok_inn       )
    );
    
endmodule
